﻿Function Invoke-WebRequestLessV3
{
        [cmdletbinding(DefaultParameterSetName="none")]
        Param( 
        [Parameter(Mandatory=$true)] 
        [String] 
        $Uri,

        [Parameter(Mandatory=$true)] 
        [String] 
        $Method,

        [Parameter(Mandatory=$false)] 
        [XML] 
        $Body,

        [Parameter(Mandatory=$false)] 
        [String] 
        $ContentType,

        [Parameter(Mandatory=$false)] 
        [System.Collections.Hashtable] 
        $Headers,

        [Parameter(ParameterSetName="UseDefaultCredentials", Mandatory=$true)] 
        [Switch]
        $UseDefaultCredentials,

        [Parameter(ParameterSetName="SpecificCredentials", Mandatory=$true)] 
        [System.Management.Automation.PSCredential]
        $Credential

        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Invoke-WebRequestLessV3 for $Uri"
    }
    End
    {
        write-verbose "End Invoke-WebRequestLessV3 for $Uri"
        $watch.Stop();
        Write-Verbose "function duration : $($watch.Elapsed)"
    }

    process
    {
        
        $req = [system.Net.WebRequest]::Create($Uri)
        $req.Method = $Method

        if ($Headers)
        {
            if ($Headers.Count -gt 0)
            {
                $Headers.keys | ForEach-Object {
                    $req.Headers.Add($_, $Headers[$_])
                }
            }
        }

        if ($ContentType) {$req.ContentType=$ContentType}


        if ($PSCmdlet.ParameterSetName -eq 'SpecificCredentials')
        {
            $req.Credentials = $Credential.GetNetworkCredential()
        }
        else
        {
            $req.Credentials = [System.Net.CredentialCache]::DefaultCredentials
        }


        $httpWatch = [system.diagnostics.stopwatch]::StartNew() 
        try 
        {
            if ($Body)
            {
                $requestStream = $req.GetRequestStream()
                $Body.Save($requestStream)
                $requestStream.Close()

            }

            $res = $req.GetResponse()
            [IO.Stream] $stream = $res.GetResponseStream()
            [IO.StreamReader] $reader = New-Object IO.StreamReader($stream)
            [string] $output = $reader.readToEnd()
            $stream.flush()
            $stream.close()

            $res | Add-Member -MemberType NoteProperty -Name Content -Value $output

        } 
        catch [System.Net.WebException] 
        {
            $res = $_.Exception.Response
        }
        finally
        {
            if ($stream)
            {
                $stream.Close()
                $stream.Dispose()
            }

            $httpWatch.Stop();

            $res
        }

    }
}




function Test-WebRequest
{
    [cmdletbinding(DefaultParameterSetName="none")]
    param
    (
        [Parameter(Mandatory=$true)] 
        [String]
        $MonitoringCode,

        [Parameter(Mandatory=$false)] 
        [String]
        $Environment,

        [Parameter(Mandatory=$true)] 
        [String]
        $ContractName,

        [Parameter(Mandatory=$false)] 
        [String]
        $Node,

        [Parameter(ParameterSetName="MonitoringDatasource", Mandatory=$true)] 
        [ValidateScript({(Test-Path $_ -PathType Leaf)})] 
        [String]
        $MonitoringDatasourceFilePath="MonitoringWebService.Contracts.Xml",

        [Parameter(Mandatory=$false)] 
        [String]
        $MonitoringKeyFilePath

    )
    Process
    {

        #Extract Monitoring configuration from Xml file
        $xmlCOnfiguration = Select-Xml -Path $MonitoringDatasourceFilePath -XPath "//webRequestMonitoring[@monitoringCode='$MonitoringCode']"

        #Load users definition
        $securityDefinition = $null
        $xmlSecurity = Select-Xml -Path $MonitoringDatasourceFilePath -XPath "//security"
        if ($xmlSecurity) {$securityDefinition = New-DynamicObject_v2 -Source $xmlSecurity.Node}


        if (!$xmlCOnfiguration)
        {
            Throw "Missing Monitoring Definition for '$MonitoringCode'"
        } 

        #Extract Contract
        $monitoringContract = $xmlCOnfiguration.Node.SelectSingleNode("monitoringContracts/monitoringContract[@name='$ContractName']")
        if (!$monitoringContract)
        {
            Throw "Missing Monitoring contract for '$WebServiceCode' and contract name '$ContractName'"
        }

        $headers = @{}
        $monitoringContract.selectNodes("headers/header") | ForEach-Object {$headers.Add($_.key,$_.Value)}
        $headers.keys | ForEach-Object {
            write-verbose "Key : '$($_)' => Value : '$($headers.$_)'"
        }

        #Extract Url
        $searchPattern = ""
        $searchEnvironment=""
        $searchNode=""

        if ($Environment) {$searchPattern = "@environment='$Environment'"}
        if ($Node) 
        {
            if ($searchPattern) {$searchPattern +=" and @node='$Node'"}
            else {$searchPattern = "@node='$Node'"}
        }
        if ($searchPattern) {$searchPattern = "[$searchPattern]"}

        $endPoints = @()
        $xmlCOnfiguration.Node.SelectNodes("endPoint$searchPattern") | ForEach-Object {$endPoints += $_}

        if ($endPoints.count -eq 0) 
        {
            Throw "Missing endpoint to monitor for this definition 'endPoint$searchPattern'"
        }

        $httpWatch = [system.diagnostics.stopwatch]::StartNew() 
        $httpWatch.Stop()

        #Call the Test-SoapRequest with monitoring definition found in the Monitoring Datasource file
        foreach($endPoint in $endPoints)
        {
            
            #Create the Full Uri
            $relativeUri = $monitoringContract.relativeUri
            if ($relativeUri.StartsWith("/")){$relativeUri = $relativeUri.Substring(1)}
            $targetUri = [System.IO.Path]::Combine($endPoint.Uri, $relativeUri)
            $targetUri = $targetUri.replace("\","/")
            Write-Verbose "Target Uri : '$targetUri'"
            $targetUri = [Uri]$targetUri


            $contentType = $null
            if ($monitoringContract.contentType) {$contentType = $monitoringContract.ContentType}
            Write-Verbose "Content Type : '$contentType'"

            $body = $null
            if ($monitoringContract.body.'#cdata-section') {$body = [XML]$monitoringContract.body.'#cdata-section'}
            Write-Verbose "Body : `n'$body'"

            $method = "default"
            if ($monitoringContract.method) {$method = $monitoringContract.method}
            Write-Verbose "method : `n'$method'"

            $overrideSPN = $null
            if ($endPoint.overrideSPN)
            {
                
                $customhost = "{0}://{1}" -f $targetUri.Scheme, $targetUri.Host
                
                if ($targetUri.AbsoluteUri.Contains(":$($targetUri.Port)"))
                {
                    $customhost += ":$($targetUri.Port)"
                }
                $customhost += "/" 

                if ([System.Net.AuthenticationManager]::CustomTargetNameDictionary.ContainsKey($customhost))
                {
                    [System.Net.AuthenticationManager]::CustomTargetNameDictionary[$customhost] = $endPoint.overrideSPN
                }
                else
                {
                    [System.Net.AuthenticationManager]::CustomTargetNameDictionary.Add($customhost, $endPoint.overrideSPN)
                }
            }

            $httpWatch.Start()
            try
            {

                $userCredential = $null
                if ($endPoint.lookupUser)
                {
                    #Search user
                    if (!$securityDefinition)
                    {
                        throw "security definition is missing and specific user '$endPoint.lookupUser'  is define to execute the monitoring"
                    }

                    if (!$securityDefinition.users.ContainsKey($endPoint.lookupUser))
                    {
                        throw "Lookup user '$endPoint.lookupUser'  is missing in the users definition list"
                    }

                    $userDefinition = $securityDefinition.Users.$($endPoint.lookupUser)

                    if ($userDefinition.passwordEncrypted)
                    {
                        if (!$MonitoringKeyFilePath)
                        {
                            throw "There is no monitoring Key file to decrypt the password"                            
                        }

                        $userCredential = Get-UserCredential -KeyFilePath $MonitoringKeyFilePath -UserName $userDefinition.Account -EncryptedPassword $userDefinition.password
                    }
                    else
                    {
                        $userCredential = Get-UserCredential -UserName $userDefinition.Account -ClearPassword $userDefinition.password
                    }

                }

                if ($UserCredential -eq $null)
                {
                    write-verbose "use default Credential to call the web service" 
                    $requestResponse = Invoke-WebRequest -Uri $targetUri -UseDefaultCredentials -Headers $headers -Method $method -Body $body -ContentType $contentType -UseBasicParsing
                    #$requestResponse = Invoke-WebRequestLessV3 -Uri $targetUri -UseDefaultCredentials -Headers $headers -Method $method -Body $body -ContentType $contentType

                }
                else
                {
                    write-verbose "Use specific Credential '$($UserCredential.UserName)' to call the web service" 
                    $requestResponse = Invoke-WebRequest -Uri $targetUri -Headers $headers -Method $method -Body $body -ContentType $contentType -Credential $userCredential -UseBasicParsing
                }        
            }
            catch [System.Net.WebException] 
            {
                if ($_.Exception.Response) {$requestResponse = $_.Exception.Response}
                else
                {
                    $requestResponse = New-Object PSObject
                    $requestResponse | add-member -MemberType NoteProperty -Name StatusCode -Value $_.Exception.Status
                    $requestResponse | add-member -MemberType NoteProperty -Name StatusDescription -Value $_.Exception.Message
                }
            }
            catch 
            {
                $requestResponse = New-Object PSObject
                $requestResponse | add-member -MemberType NoteProperty -Name StatusCode -Value $_.Exception.Gettype().Name
                $requestResponse | add-member -MemberType NoteProperty -Name StatusDescription -Value $_.Exception.Message
            }
            $httpWatch.Stop()
            $requestResponse | Add-Member -MemberType NoteProperty -Name Duration -Value $httpWatch.Elapsed -Force
            
            $requestResponse | Add-Member -MemberType NoteProperty -Name WebUri -Value $targetUri
            $requestResponse | Add-Member -MemberType NoteProperty -Name ContractName -Value $ContractName
            $requestResponse | Add-Member -MemberType NoteProperty -Name MonitoringCode -Value $MonitoringCode
            $requestResponse | Add-Member -MemberType NoteProperty -Name Environment -Value $endPoint.Environment
            $requestResponse | Add-Member -MemberType NoteProperty -Name Node -Value $endPoint.Node
            $requestResponse | Add-Member -MemberType NoteProperty -Name DMZ -Value $endPoint.dmz
            $requestResponse | Add-Member -MemberType NoteProperty -Name Application -Value $xmlCOnfiguration.Node.Application
            $requestResponse | Add-Member -MemberType NoteProperty -Name SpecificUser -Value $endPoint.lookupUser
            $requestResponse | Add-Member -MemberType NoteProperty -Name OverrideSPN -Value $endPoint.overrideSPN

            $requestResponse

        }

    }
}
